package com.cloud.utils.hutool;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.cloud.utils.hutool.annos.HuExcelAno;
import com.cloud.utils.hutool.objs.HuExcelInnerClass;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.apache.commons.lang3.time.DateUtils;
import org.apache.poi.ss.usermodel.*;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.io.File;
import java.lang.reflect.Field;
import java.util.*;

/**
 * @author: mengxiangzhong
 * @date: 2023/3/16 9:18
 * @TODO: hutool的excel拓展工具类
 **/

public class ExcelExTendUtils {

    /**
     * 普通导出列合并单元格
     * @param writer 写入对象
     * @param rowNum 第多少列
     * @param dataList 单个数据列
     * @param mergeList 针对数据列每一个数据所占的行数
     */
    public static void   writerComColMerge(ExcelWriter writer,Integer rowNum, List<String> dataList, List<Integer> mergeList){
        int index = 0;
        for(int i = 0 ; i < mergeList.size() ; i++){
            if(mergeList.get(i) > 1){
                writer.merge(rowNum,rowNum,index,index+mergeList.get(i)-1 ,dataList.get(i),false);
            }else {
                writer.writeCellValue(index,rowNum,dataList.get(i));
            }
            index += mergeList.get(i);
        }
    }

    /**
     * 普通导出行合并单元行
     * @param writer  入对象
     * @param startRowNum 开始的行数
     * @param dataList 数据
     * @param mergeList 针对数据行每一个数据所占的行数
     */
    public static void  writerRowMerge(ExcelWriter writer, Integer startRowNum,  List<String> dataList, List<Integer> mergeList){
        for(int i = 0 ; i < mergeList.size() ; i++){
            if(mergeList.get(i) > 1){
                writer.merge(startRowNum,startRowNum+mergeList.get(i)-1,i,i,dataList.get(i),false);
            }else {
                writer.writeCellValue(i,startRowNum,dataList.get(i));
            }
        }
    }

    /**
     * 设置单元格
     * @param writer 写入对象
     * @param x 列坐标
     * @param y 行坐标
     * @param backgroundColorIndex 背景色：IndexedColors
     * @param font 字体
     */
    public static  void  setCell(ExcelWriter writer,int x,int y,short backgroundColorIndex,Font font){
        //位置、水平居中、垂直居中
        CellStyle cellStyle = writer.createCellStyle(x, y);
        cellStyle.setAlignment(HorizontalAlignment.CENTER);
        cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
        // 顶边栏
        cellStyle.setBorderTop(BorderStyle.THIN);
        cellStyle.setTopBorderColor(IndexedColors.BLACK.getIndex());
        // 右边栏
        cellStyle.setBorderRight(BorderStyle.THIN);
        cellStyle.setRightBorderColor(IndexedColors.BLACK.getIndex());
        // 底边栏
        cellStyle.setBorderBottom(BorderStyle.THIN);
        cellStyle.setBottomBorderColor(IndexedColors.BLACK.getIndex());
        // 左边栏
        cellStyle.setBorderLeft(BorderStyle.THIN);
        cellStyle.setLeftBorderColor(IndexedColors.BLACK.getIndex());
        // 填充前景色(两个一起使用)
        cellStyle.setFillForegroundColor(backgroundColorIndex);
        cellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
        //设置字体
        if(font != null){
            cellStyle.setFont(font);
        }

    }


    /**
     * 对象导出
     * @param writer
     * @param title 标题
     * @param dataList 数据
     * @param tClass 对象
     * @param <T>
     */
    public static <T> void exportObj(ExcelWriter writer,String title,List<T> dataList, Class<T> tClass){
        //1.通过反射获取到对象的注解
        Integer totalRows  = 0 ;
        List<HuExcelInnerClass>  firstObjList = getAnosFromClass(tClass);
        for(HuExcelInnerClass  f : firstObjList){
            HuExcelAno ano = f.getHuExcelAno();
            List<HuExcelInnerClass>  temList =  ano.type() == 1 ? Arrays.asList(f):getAnosFromClass(f.gettClass());
            totalRows += temList.size();
            if(temList.size() > 1){
                f.setChildFields(temList);
            }
        }

        //2.构建title
        Integer beginNum = 0;
        if(title != null && title.trim().length() != 0){
            writer.merge(totalRows -1 ,title);
            writer.passCurrentRow();
            beginNum++;
        }

        //3.合并表头
        Map<String,HuExcelInnerClass>  fieldMap = new LinkedHashMap<>();
        Boolean hasTwoFlag = totalRows > firstObjList.size();
        List<String>  oneData = new ArrayList<>();
        List<Integer> oneIndex = new ArrayList<>();
        List<String>  twoData = new ArrayList<>();
        List<Integer> twoIndex = new ArrayList<>();
        for(HuExcelInnerClass  f : firstObjList){
            fieldMap.put(f.getFieldName(),f);
            HuExcelAno ano = f.getHuExcelAno();
            List<HuExcelInnerClass>  temList =  ano.type() == 1 ? Arrays.asList(f):getAnosFromClass(f.gettClass());
            oneData.add(ano.name());
            oneIndex.add(temList.size());
            if(!hasTwoFlag){
                continue;
            }
            if(ano.type() == 1){
                twoData.add(ano.name());
                twoIndex.add(1);
            }else {
                for(HuExcelInnerClass tem : temList){
                    if(tem.getHuExcelAno() != null){
                        twoData.add(tem.getHuExcelAno().name());
                        twoIndex.add(1);
                    }
                }
            }

        }
        writerComColMerge(writer,beginNum,oneData,oneIndex);
        beginNum++;
        if(!CollectionUtils.isEmpty(twoData)){
            writerComColMerge(writer,beginNum,twoData,twoIndex);
        }
        writer.passCurrentRow();

        //4.构建数据
        List<List<String>> rowList = new ArrayList<>();
        HuExcelInnerClass he ;
        HuExcelAno ano;
        for(T  data : dataList){
            List<String> oneRow = new ArrayList<>();
            Map<String,?> map = BeanUtil.beanToMap(data);
            for(Map.Entry<String,HuExcelInnerClass> e : fieldMap.entrySet() ){
                he = e.getValue();
                ano = he.getHuExcelAno();
                Object tempData =  map.get(he.getFieldName());
                if(ano.type() == 1 ){
                    if(tempData == null){
                        oneRow.add("");
                        continue;
                    }
                    if(ano.format()){
                        oneRow.add(DateUtil.format((Date)tempData,he.getJsonFormat().pattern()));
                    }else {
                        oneRow.add(tempData.toString());
                    }
                    continue;
                }
                //使用对象
                Object childObj = map.get(he.getFieldName());
                Map<String,?> childMap = BeanUtil.beanToMap(childObj);
                for(HuExcelInnerClass child : e.getValue().getChildFields()){
                    Object childTemp = childMap.get(child.getFieldName());
                    if(childTemp == null){
                        oneRow.add("");
                        continue;
                    }
                    if(ano.format()){
                        oneRow.add(DateUtil.format((Date)childTemp,he.getJsonFormat().pattern()));
                    }else {
                        oneRow.add(childTemp.toString());
                    }
                }
            }
            rowList.add(oneRow);
        }
        writer.write(rowList,true);
    }

    /**
     * 反射获取到对象的注解
     * @param tClass
     * @param <T>
     * @return
     */
    private static  <T> List<HuExcelInnerClass>  getAnosFromClass(Class<T> tClass){
        List<HuExcelInnerClass> list = new ArrayList<>();
        Field[] fields = tClass.getDeclaredFields();
        for(Field f : fields){
            HuExcelAno ano = f.getAnnotation(HuExcelAno.class);
            if(ano == null){
                continue;
            }
            HuExcelInnerClass innerClass = new HuExcelInnerClass(ano,f.getName());
            if(ano.format()){
                innerClass.setJsonFormat(f.getAnnotationsByType(JsonFormat.class)[0]);
            }
            if(ano.type() == 2){
                innerClass.settClass( f.getType());
            }
            list.add(innerClass);
        }
        return list;
    }






}
